package beaver.game;

import java.util.Random;

import javax.swing.JOptionPane;

import org.jbox2d.collision.WorldManifold;
import org.jbox2d.common.Vec2;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.ImageBuffer;
import org.newdawn.slick.Input;
import org.newdawn.slick.MouseListener;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.fizzy.Body;
import org.newdawn.fizzy.Circle;
import org.newdawn.fizzy.CollisionEvent;
import org.newdawn.fizzy.CompoundShape;
import org.newdawn.fizzy.DynamicBody;
import org.newdawn.fizzy.Polygon;
import org.newdawn.fizzy.Rectangle;
import org.newdawn.fizzy.Shape;
import org.newdawn.fizzy.StaticBody;
import org.newdawn.fizzy.World;
import org.newdawn.fizzy.WorldListener;

import beaver.game.GameplayState.Item;

@SuppressWarnings("rawtypes")
public class GameplayState extends BasicGameState implements WorldListener,
		MouseListener {
	
	protected int stateID = -1;
	protected World world;
	protected float scale = 1f; // the scale of what is displayed

	// Animations used for the beaver (not yet loaded)
	protected Animation team1Moving = null;
	protected Animation team1MovingR = null;
	protected Animation team2Moving = null;
	protected Animation team2MovingR = null;
	float beaverX, beaverY;

	protected float worldCenterX;
	protected float worldCenterY;
	protected float crosshairAngle;
	protected Body crosshair;
	protected Image crosshairImage;
	protected Body bullet;
	protected Image bulletImage;
	protected Image healthImage;
	protected Image ammoImage;
	protected Body boundary;

	protected float collisionX = 0f;
	protected float collisionY = 0f;
	protected float leftmostXFromExplosion = 1000000f;
	protected float rightmostXFromExplosion = -1;
	
	protected ImageBuffer terrainy;
	protected Image terrainyImage;
	protected float[][] terrainBitmap;
	protected CompoundShape[] terrain = new CompoundShape[1];
	protected CompoundShape compoundTerrain;
	protected StaticBody terrainBody = new StaticBody(new Circle(0), 0, 0);
	protected boolean drawCircle = false;

	protected String p1Name = "Player 1";
	protected String p2Name = "Player 2";
	protected Player p1 = null;
	protected Player p2 = null;
	protected Player currentPlayer = null;
	
	protected long startTime = 0;
	protected long endTime = 0;
	protected int endTurn = -1;
	
	protected int radius = 40;
	protected boolean cameraOnBullet = false;
	
	protected boolean powerSeqEngaged = false;
	protected int power = 1;
	protected Item[] item;
	protected boolean killBullet;

	protected Image bg_sprite = null;

	public GameplayState(int stateID) {
		this.stateID = stateID;
	}
	
	public GameplayState(int stateID, String remote, boolean isHost){
		this.stateID = stateID;
	}
	
	@Override
	public int getID() {
		return stateID;
	}
	
	//an item class. Has the body and the type.
	//type 1 is hp, type 2 is ammo
	protected class Item {
		private int value;
		public DynamicBody body;
		public boolean remove = false;
		protected Item() {
			int[] coord = randomFreeCoordinate(20);
			if (coord[0] < 0) this.body = new DynamicBody(new Circle(10.0f), -100, 1300);
			else this.body = new DynamicBody(new Circle(10.0f), (coord[0]-terrainyImage.getWidth()/2), (coord[1]-terrainyImage.getHeight()/2));
			this.value = 1;
			this.remove = false;
		}
		public void setTypeHP(){
			this.value = 1;
		}
		public void setTypeAmmo(){
			this.value = 2;
		}
		public boolean isTypeHP(){
			if (this.value == 1) return true;
			return false;
		}
		public boolean isTypeAmmo(){
			if (this.value == 2) return true;
			return false;
		}
		public DynamicBody getBody(){
			return this.body;
		}
	}

	@Override
	public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException {
		
		
		bg_sprite.draw();
		//g.setBackground(new Color(0,61,245));
		g.setAntiAlias(true);
		g.setColor(Color.white);
	
		g.pushTransform();
		g.translate(worldCenterX, worldCenterY);
		g.scale(scale, -scale);
		g.drawImage(terrainyImage, -terrainyImage.getWidth()/2, -terrainyImage.getHeight()/2);
		g.popTransform();

		g.pushTransform();
		g.translate(worldCenterX, worldCenterY);
		g.scale(scale, -scale);

		for (int i = 0; i < world.getBodyCount(); i++) {
			Body body = world.getBody(i);
			if (!body.equals(terrainBody) && !body.equals(boundary)) {
				g.setColor(Color.black);
				drawBody(g, body);
			}
		}
		g.popTransform();
		
		g.setColor(Color.white);
		placeCrosshairs();
		
		g.drawString(
				"Left/right to move, space to jump, up/down to aim, enter to shoot."
						+ "\nZ and X to zoom in and out, and R to reset the crosshair."
						+ "\nW,A,S,D to pan, and ESC to pause.",
				10, 25);
		g.drawString(
				"\n Time Remaining: "
						+ (int)((endTime - System.currentTimeMillis()) / 1000f), 30,
				500);
		drawStats(g);
	
	}
	
	protected void placeCrosshairs(){
		Body currentBeaverBody = currentPlayer.getCurrentBeaver().getBody();
		world.remove(crosshair);
		float x = (float) (currentBeaverBody.getX() + 100*Math.sin(Math.toRadians(crosshairAngle)));
		float y = (float) (currentBeaverBody.getY() + 100*Math.cos(Math.toRadians(crosshairAngle)));
		crosshair = new StaticBody(new Circle(10),x,y);
		world.add(crosshair);
		crosshair.setActive(false);
	}
	
	protected void drawBody(Graphics g, Body body) {
		Shape shape = body.getShape();
		drawShape(g, body, shape);
	}

	private void drawShape(Graphics g, Body body, Shape shape) {
		if (shape instanceof Rectangle)
			drawRectangle(g, body, (Rectangle) shape);
		if (shape instanceof Circle)
			drawCircle(g, body, (Circle) shape);
		if (shape instanceof Polygon)
			drawPolygon(g, body, (Polygon) shape);
		if (shape instanceof CompoundShape)
			drawCompound(g, body, (CompoundShape) shape);
	}

	private void drawCompound(Graphics g, Body body, CompoundShape shape) {
		int count = shape.getShapeCount();
		for (int i = 0; i < count; i++) {
			drawShape(g, body, shape.getShape(i));
		}
	}

	private void drawPolygon(Graphics g, Body body, Polygon shape) {
		g.pushTransform();
		g.translate(body.getX(), body.getY());
		g.rotate(0, 0, (float) Math.toDegrees(body.getRotation()));
		g.translate(shape.getXOffset(), shape.getYOffset());
		g.rotate(0, 0, (float) Math.toDegrees(shape.getAngleOffset()));

		for (int i = 0; i < shape.getPointCount(); i++) {
			int n = i + 1;
			if (n >= shape.getPointCount()) {
				n = 0;
			}
			g.drawLine((int) shape.getPointX(i), (int) shape.getPointY(i),
					(int) shape.getPointX(n), (int) shape.getPointY(n));
		}
		g.popTransform();
	}

	private void drawCircle(Graphics g, Body body, Circle shape) {
		g.pushTransform();
		g.translate(body.getX(), body.getY());
		float radius = shape.getRadius();
		Beaver[] p1Team = p1.getTeam();
		Beaver[] p2Team = p2.getTeam();
		
		for(int i = 0;i<Constants.BEAVERS_PER_TEAM;i++){
			if(body.equals(p1Team[i].getBody())&&p1Team[i].isAlive()){
				boolean direction = p1Team[i].getFacingR();
				if (direction == true)
					g.drawAnimation(team1MovingR, (int) -radius, (int) -radius);
				else
					g.drawAnimation(team1Moving, (int) -radius, (int) -radius);
				// Display HP over beaver's head
				Color old = g.getColor();
				g.setColor(Color.green);

				g.pushTransform();
				g.scale(scale, -scale);
				g.drawString("HP=" + p1Team[i].getHP(), (int) -radius, -team1Moving.getHeight()-10);
				
				if (currentPlayer.getCurrentBeaver().equals(p1Team[i])) {
					// Draw the power bar over the current beaver's head
					if (powerSeqEngaged) {
						g.setColor(Color.red);
						g.fillRect((int) -radius, -team1Moving.getHeight()-20, power/3, 10);
					}
				}
				
				g.popTransform();
				g.setColor(old);
				
			}

			if(body.equals(p2Team[i].getBody())&&p2Team[i].isAlive()){
				// Get current beaver's direction *GIANNI*
				boolean direction = p2Team[i].getFacingR();
				if (direction == true)
					g.drawAnimation(team2MovingR, (int) -radius, (int) -radius);
				else
					g.drawAnimation(team2Moving, (int) -radius, (int) -radius);
				// Display HP over beaver's head
				Color old = g.getColor();
				g.setColor(Color.red);
				
				g.pushTransform();
				g.scale(scale, -scale);
				g.drawString("HP=" + p2Team[i].getHP(), (int) -radius, -team2Moving.getHeight()-10);
				
				if (currentPlayer.getCurrentBeaver().equals(p2Team[i])) {
					// Draw the power bar over the current beaver's head
					if (powerSeqEngaged) {
						g.setColor(Color.red);
						g.fillRect((int) -radius, -team1Moving.getHeight()-20, power/3, 10);
					}
				}
				
				g.popTransform();
				g.setColor(old);
			}
		}
		
		if (body.equals(crosshair)){
			g.drawImage(crosshairImage, (int) -radius, (int) -radius);
		}
		
		if (body.equals(bullet)){
			g.drawImage(bulletImage, (int) -radius, (int) -radius);
		}
		
		for (int i = 0; i < item.length; i++){
			if (body.equals(item[i].getBody())){
				if(!item[i].isTypeHP())
					g.drawImage(healthImage, (int) -radius, (int) -radius);
				else
					g.drawImage(ammoImage, (int) -radius, (int) -radius);
			}
		}
		/*g.rotate(0,0,(float) Math.toDegrees(body.getRotation()));
		g.drawOval((int) -radius,(int) -radius,(int) (radius*2),(int) (radius*2));
		g.drawLine(0,0,0,(int) -radius);*/
		
		g.popTransform();
	}
	
	protected void nextPlayer(){
		if(currentPlayer==p1){
			currentPlayer=p2;
		}
		else{
			currentPlayer=p1;
		}
		currentPlayer.nextBeaver();
	}

	private void drawRectangle(Graphics g, Body body, Rectangle shape) {
		g.pushTransform();
		g.translate(body.getX(), body.getY());
		g.rotate(0, 0, (float) Math.toDegrees(body.getRotation()));
		g.translate(shape.getXOffset(), shape.getYOffset());
		g.rotate(0, 0, (float) Math.toDegrees(shape.getAngleOffset()));

		float width = shape.getWidth();
		float height = shape.getHeight();

		if ((body.getUserData()) != null) {
			g.fillRect((int) -(width / 2), (int) -(height / 2), (int) width,
					(int) height);
		} else {
			g.drawRect((int) -(width / 2), (int) -(height / 2), (int) width,
					(int) height);
		}
		g.popTransform();
	}

	@SuppressWarnings("unchecked")
	@Override
	public void init(GameContainer gc, StateBasedGame sbg) throws SlickException {
		
		try {
			bg_sprite = new Image("data/8bitBGgood.gif");
		} catch (SlickException e) {
			e.printStackTrace();
		}
		
		gc.setTargetFrameRate(Constants.FRAME_RATE);
		gc.setVSync(true);
		worldCenterX = gc.getWidth() / 2;
		worldCenterY = gc.getHeight() / 2;
		world = new World();
		world.setIterations(Constants.ITERATIONS);
		world.setGravity(-10);

		p1 = new Player(p1Name);
		p2 = new Player(p2Name);
		
		terrainy = new ImageBuffer(Constants.MAX_WIDTH, Constants.MAX_HEIGHT*2);
		
		terrainBitmap = Noise.GenerateWhiteNoise(Constants.MAX_WIDTH,Constants.MAX_HEIGHT*2);
		terrainBitmap = Noise.GenerateSmoothNoise(terrainBitmap,7);
		terrainBitmap = Noise.GeneratePerlinNoise(terrainBitmap,7);
		
		SlickDisplay();
		terrainyImage = terrainy.getImage();
		createTerrainBody2(-terrainyImage.getWidth()/2, -terrainyImage.getHeight()/2);
		
		for (int i = 0; i < Constants.BEAVERS_PER_TEAM; i++) {
			int[] coord = randomFreeCoordinate(20);
			if (coord[0] < 0) p1.getTeam()[i].setBody(new DynamicBody(new Circle(10.0f), -100*i, 1300));
			else p1.getTeam()[i].setBody(new DynamicBody(new Circle(10.0f), (coord[0]-terrainyImage.getWidth()/2), (coord[1]-terrainyImage.getHeight()/2)));
			p1.getTeam()[i].getBody().setDensity(1);
			p1.getTeam()[i].getBody().setFriction(1000);
			p1.getTeam()[i].getBody().setRestitution(0f);
			p1.getTeam()[i].getBody().setAngularDamping(1f);
			p1.getTeam()[i].getBody().setFixedRotation(true);
			world.add(p1.getTeam()[i].getBody());
		}
		
		for (int i = 0; i < Constants.BEAVERS_PER_TEAM; i++) {
			int[] coord = randomFreeCoordinate(20);
			if (coord[0] < 0) p2.getTeam()[i].setBody(new DynamicBody(new Circle(10.0f), -100*i, 1300));
			else p2.getTeam()[i].setBody(new DynamicBody(new Circle(10.0f), (coord[0]-terrainyImage.getWidth()/2), (coord[1]-terrainyImage.getHeight()/2)));
			p2.getTeam()[i].getBody().setDensity(1);
			p2.getTeam()[i].getBody().setFriction(1000);
			p2.getTeam()[i].getBody().setRestitution(0f);
			p2.getTeam()[i].getBody().setAngularDamping(1f);
			p2.getTeam()[i].getBody().setFixedRotation(true);
			world.add(p2.getTeam()[i].getBody());
		}

		currentPlayer = p1;
		
		//Add the invisible boundary
		boundary = addBoundary(Constants.MAX_WIDTH*2,Constants.MAX_HEIGHT*3);
		boundary.setRestitution(0f);
		world.add(boundary);
		world.addBodyListener(boundary, this);
		
		// load beaver images to use in our animation
		Image beaver = new Image("data/bazooka4.png");
		Image beaver2 = new Image("data/bazooka5.png");
		Image[] frames = {beaver.getFlippedCopy(false, true).getScaledCopy(0.5f), beaver2.getFlippedCopy(false, true).getScaledCopy(0.5f)};
		Image[] frames2 = {beaver.getFlippedCopy(true, true).getScaledCopy(0.5f), beaver2.getFlippedCopy(true, true).getScaledCopy(0.5f)};
		team1Moving = new Animation(frames, 1000, false); // duration in ms
		team1MovingR = new Animation(frames2, 1000, false); // duration in ms
		Image beaver3 = new Image("data/darkBazooka4.png");
		Image beaver4 = new Image("data/darkBazooka5.png");
		Image[] frames3 = {beaver3.getFlippedCopy(false, true).getScaledCopy(0.5f), beaver4.getFlippedCopy(false, true).getScaledCopy(0.5f)};
		Image[] frames4 = {beaver3.getFlippedCopy(true, true).getScaledCopy(0.5f), beaver4.getFlippedCopy(true, true).getScaledCopy(0.5f)};
		team2Moving = new Animation(frames3, 1000, false); // duration in ms
		team2MovingR = new Animation(frames4, 1000, false); // duration in ms
		
		// add the mouse listener
		gc.getInput().addMouseListener(this);
		
		//add two items to the world
		item = new Item[]{new Item(), new Item()};
		item[0].setTypeAmmo();
		item[1].setTypeHP();
		item[0].getBody().setFriction(1000);
		item[1].getBody().setFriction(1000);
		item[0].getBody().setRestitution(0);
		item[1].getBody().setRestitution(0);
		item[0].getBody().setFixedRotation(true);
		item[1].getBody().setFixedRotation(true);
		world.add(item[0].getBody());
		world.add(item[1].getBody());
		world.addBodyListener(item[0].getBody(), this);
		world.addBodyListener(item[1].getBody(), this);
		
		Body currentBeaverBody = currentPlayer.getCurrentBeaver().getBody();
		crosshairAngle = 45;
		crosshair = new StaticBody(new Rectangle(50f,5f),currentBeaverBody.getX(),currentBeaverBody.getY());
		world.add(crosshair);
		crosshair.setRotation(crosshairAngle);
		crosshairImage = new Image("data/crosshair.png").getScaledCopy(0.5f);
		bulletImage = new Image("data/wood.png").getScaledCopy(0.5f);
		healthImage = new Image("data/health.png").getScaledCopy(0.5f);
		ammoImage = new Image("data/ammo.png").getFlippedCopy(false, true).getScaledCopy(0.5f);
		timer(Constants.SECONDS_PER_TURN);
	}

	protected void timer(long countdown) {
		startTime = System.currentTimeMillis();
		endTime = startTime + (countdown * 1000);
	}
	
	private void endTurn(GameContainer gc){
		System.out.println("End the turn");
		Body currentBeaverBody = currentPlayer.getCurrentBeaver().getBody();
		if (currentBeaverBody.isAttached()) currentBeaverBody.setActive(true);
		//Cycle Player
		nextPlayer();
		currentBeaverBody = currentPlayer.getCurrentBeaver().getBody();
		worldCenterX = gc.getWidth()/2 - (currentBeaverBody.getX()*scale);
		worldCenterY = gc.getHeight()/2 - (currentBeaverBody.getY()*-scale);
		timer(Constants.SECONDS_PER_TURN);
	}
	
	protected void drawStats(Graphics g){
		g.drawString("\nPlayer: " + currentPlayer.getName(),300,460);
		g.drawString("\nBeaver: " + currentPlayer.getCurrentBeaver().getName(),300,480);
		g.drawString("\nWeapon: " + currentPlayer.getCurrentBeaver().getCurrentWeapon().getName(), 300, 500);
		g.drawString("\nAmmo: " + currentPlayer.getCurrentBeaver().getCurrentWeapon().getAmmo(), 300, 520);
		g.drawString("\nHP: " + currentPlayer.getCurrentBeaver().getHP(), 300, 540);
	}
	
	protected void createTerrainBody2(float x0, float y0) {
		if (terrain.length == 1) terrain = new CompoundShape[Constants.MAX_WIDTH];
		for (int x = 0; x < Constants.MAX_WIDTH; x++) {
			if (x < leftmostXFromExplosion && leftmostXFromExplosion != 1000000f) continue;
			if (x > rightmostXFromExplosion && rightmostXFromExplosion != -1f) continue;

			Polygon temp = new Polygon();
			CompoundShape compoundTemp = new CompoundShape();
			Vec2 firstPoint = null;
			Vec2 lastPoint = null;

			for (int y = 0; y < Constants.MAX_HEIGHT * 2; y++) {
				if (terrainBitmap[x][y] > 0.5) {
					if (firstPoint == null)
						firstPoint = new Vec2(x, y);
					else
						lastPoint = new Vec2(x, y);
				} else {
					if (firstPoint != null) {
						if (lastPoint == null)
							lastPoint = new Vec2(firstPoint.x + 0.1f,
									firstPoint.y);
						temp.setPoints(new Vec2[] { firstPoint, lastPoint });
						compoundTemp.add(temp);
						temp = new Polygon();
						firstPoint = null;
						lastPoint = null;
					}
				}
			}
			if (firstPoint != null) {
				if (lastPoint == null)
					lastPoint = new Vec2(firstPoint.x + 0.1f, firstPoint.y);
				temp.setPoints(new Vec2[] { lastPoint, firstPoint });
				compoundTemp.add(temp);
				temp = new Polygon();
				firstPoint = null;
				lastPoint = null;
			}
			terrain[x] = compoundTemp;
			compoundTemp = new CompoundShape();

		}
		
		if (terrainBody.isAttached()){
			world.remove(terrainBody);
		}
		
		compoundTerrain = new CompoundShape();
		for (int i = 0; i < Constants.MAX_WIDTH; i++){
			compoundTerrain.add(terrain[i]);
		}		
		
		terrainBody = new StaticBody(compoundTerrain, x0, y0);
		terrainBody.setFriction(1000);
		terrainBody.setRestitution(0.1f);

		world.add(terrainBody);
		leftmostXFromExplosion = 1000000f;
		rightmostXFromExplosion = -1;
	}

	protected void SlickDisplay() {
		for (int x = 0; x < Constants.MAX_WIDTH; x++) {
			for (int y = 0; y < Constants.MAX_HEIGHT * 2; y++) {
				if (terrainBitmap[x][y] > 0.5) {
					terrainy.setRGBA(x, y, 184, 138, 0, (int)(255*terrainBitmap[x][y]));
					if (terrainBitmap[x][y] < 0.60) 
						terrainy.setRGBA(x, y, 0, 184, 46, 255);
				} else
					terrainy.setRGBA(x, y, 0, 0, 0, 0);
			}
		}
	}

	/**Returns a random location with a circle of a certain radius free
	 * */
	protected int[] randomFreeCoordinate(int radius){
		//set a boolean variable for whether there's a bottom below the found spot
		//also, a counter in case things go on for too long
		boolean hasBottom = false;
		//first pick a random point, and see if it's free in the grid
		Random gen = new Random();
		int x0 = gen.nextInt(Constants.MAX_WIDTH);
		int y0 = gen.nextInt(Constants.MAX_HEIGHT * 2);
		//Loop until we find a spot (give up after 1000 tries just in case)
		for(int i = 0; i < 1000; i++){
			//make sure there's a bottom to the spot we found
			if (terrainBitmap[x0][y0] < 0.5){
				for (int y = y0; y > 0; y--) {
					if (terrainBitmap[x0][y] > 0.5) hasBottom = true;
				}
				//then see if there's a square of the given distance around the spot we found
				try{
					if ((terrainBitmap[x0+radius][y0+radius] < 0.5) &&
						(terrainBitmap[x0-radius][y0+radius] < 0.5) &&
						(terrainBitmap[x0+radius][y0-radius] < 0.5) &&
						(terrainBitmap[x0-radius][y0-radius] < 0.5) &&
						hasBottom){ 	
					return new int[]{x0,y0};
					}
				} catch(Exception e){
				}
				hasBottom = false;
			}
			x0 = gen.nextInt(Constants.MAX_WIDTH);
			y0 = gen.nextInt(Constants.MAX_HEIGHT * 2);
			}
		return new int[]{-1,-1};
	}
	
	/**
	 * Adds a circular impact to the terrainBitmap and redraws
	 */
	protected void addExplosionToTerrain(int x0, float y0, int radius) {
		for (int x = 0; x < Constants.MAX_WIDTH; x++) {
			for (int y = 0; y < Constants.MAX_HEIGHT * 2; y++) {
				if (terrainBitmap[x][y] > 0.5) {
					double distance = Math.sqrt(Math.pow(x - terrainyImage.getWidth()/2 - x0, 2)
							+ Math.pow(y - terrainyImage.getHeight()/2 - y0, 2));
					if (distance < radius) {
						terrainBitmap[x][y] = 0;
						if (x < leftmostXFromExplosion) leftmostXFromExplosion = x;
						if (x > rightmostXFromExplosion) rightmostXFromExplosion = x;
					}
				}
			}
		}
		createTerrainBody2(-terrainyImage.getWidth()/2, -terrainyImage.getHeight()/2);
		SlickDisplay();
		terrainyImage = terrainy.getImage();
	}

	@Override
	public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
		
		if(currentPlayer.hasLost()){
			if(currentPlayer==p1){
				JOptionPane.showMessageDialog(null,"Player 2 Wins!");
			}
			else{
				JOptionPane.showMessageDialog(null,"Player 1 Wins!");
			}
			sbg.enterState(BeaversGame.MAINMENUSTATE);
		}
		//if necessary remove beaver bodies
		Beaver[] p1Team = p1.getTeam();
		Beaver[] p2Team = p2.getTeam();
		for(int i=0;i<Constants.BEAVERS_PER_TEAM;i++){
			if(!p1Team[i].isAlive() && p1Team[i].getBody().isAttached()){
				p1Team[i].getBody().setActive(false);
				world.remove(p1Team[i].getBody());
			}
			if(!p2Team[i].isAlive() && p2Team[i].getBody().isAttached()){
				p2Team[i].getBody().setActive(false);
				world.remove(p2Team[i].getBody());
			}
		}
		//if necessary, kill the bullet
		if (killBullet){			
			if (bullet.isAttached()) {
				bullet.setActive(false);
				world.remove(bullet);
			}
			killBullet = false;
		}
		//if necessary, remove an item
		for (int i = 0; i < item.length; i++){
			if (item[i].remove == true && item[i].getBody().isAttached()) {
				item[i].getBody().setActive(false);
				world.remove(item[i].getBody());
			}
		}
		//Check win condition
		Body currentBeaverBody = currentPlayer.getCurrentBeaver().getBody();
		Beaver currentBeaver = currentPlayer.getCurrentBeaver();
		this.radius = currentBeaver.getCurrentWeapon().getBlastRadius();
		
		if (powerSeqEngaged) {
			if (power >= 150) power = 0;
			else power+=4;
		}
		
		// Keep the camera centered at all times *GIANNI*
		if(!cameraOnBullet){
			if (currentBeaverBody.isAttached())
				if (!currentBeaverBody.isSleeping()){
				worldCenterX = gc.getWidth()/2 - (currentBeaverBody.getX()*scale);
				worldCenterY = gc.getHeight()/2 - (currentBeaverBody.getY()*-scale);
				}
		}
		else{
			if (bullet.isAttached()){
				worldCenterX = gc.getWidth()/2 - (bullet.getX()*scale);
				worldCenterY = gc.getHeight()/2 - (bullet.getY()*-scale);
			}
		}
		
		for (int i = 0; i < Constants.UPDATES; i++) {

			Input input = gc.getInput();
			
			if (input.isKeyPressed(Input.KEY_C)) {
				worldCenterX = gc.getWidth()/2 - (currentBeaverBody.getX()*scale);
				worldCenterY = gc.getHeight()/2 - (currentBeaverBody.getY()*-scale);
			}
			
			if (input.isKeyDown(Input.KEY_Z)) {	
				if (scale > 0.001f) {
					scale = scale - 0.001f;
					worldCenterX = gc.getWidth()/2 - (currentBeaverBody.getX()*scale);
					worldCenterY = gc.getHeight()/2 - (currentBeaverBody.getY()*-scale);
				}
			}
			
			if (input.isKeyDown(Input.KEY_X)) {
				scale = scale + 0.001f;
				worldCenterX = gc.getWidth()/2 - (currentBeaverBody.getX()*scale);
				worldCenterY = gc.getHeight()/2 - (currentBeaverBody.getY()*-scale);
			}
			
			if (input.isKeyDown(Input.KEY_W)) {
				worldCenterY += 0.2f;// /updates;
			}
			
			if (input.isKeyDown(Input.KEY_S)) {
				worldCenterY -= 0.2f;// /updates;
			}
			
			if(input.isKeyPressed(Input.KEY_R)){
				System.out.println(crosshairAngle);
				crosshairAngle = 45;
			}

			if(input.isKeyPressed(Input.KEY_Q)){
				currentPlayer.getCurrentBeaver().cycleWeapon();
			}

			if (input.isKeyDown(Input.KEY_A)) {
				worldCenterX += 0.2f;// /updates;
			}
			
			if (input.isKeyPressed(Input.KEY_O)) {
				endTurn(gc);
			}
			
			if (input.isKeyDown(Input.KEY_D)) {
				worldCenterX -= 0.2f;// /updates;
			}
			
			if (input.isKeyDown(Input.KEY_RIGHT)) {
				if(endTurn>0){
					continue;
				}

				if (crosshairAngle > 180) crosshairAngle = 180-(crosshairAngle-180);
				team1MovingR.update(delta*100);
				team2MovingR.update(delta*100);
				currentPlayer.getCurrentBeaver().setFacingR(true);
				if (currentBeaverBody.getAngularVelocity() > -1.5)
					currentBeaverBody.setAngularVelocity(currentBeaverBody
							.getAngularVelocity() - 0.05f);
				currentBeaverBody.setAngularVelocity(-1.5f);
			}
			
			if (input.isKeyDown(Input.KEY_LEFT)) {
				if(endTurn>0){
					continue;
				}
				if (crosshairAngle < 180) crosshairAngle = 180+(180-crosshairAngle);
				team1Moving.update(delta*100);
				team2Moving.update(delta*100);
				currentPlayer.getCurrentBeaver().setFacingR(false);
				if (currentBeaverBody.getAngularVelocity() < 1.5)
					currentBeaverBody.setAngularVelocity(currentBeaverBody.getAngularVelocity() + 0.05f);
				currentBeaverBody.setAngularVelocity(1.5f);
			}
			
			if (input.isKeyDown(Input.KEY_UP)){
				if(endTurn>0){
					continue;
				}
				if (crosshairAngle < 180 && crosshairAngle > 0) crosshairAngle -= 0.1f;
				else if (crosshairAngle > 180 && crosshairAngle < 360) crosshairAngle += 0.1f;
			}
			
			if (input.isKeyDown(Input.KEY_DOWN)){
				if(endTurn>0){
					continue;
				}
				if (crosshairAngle < 180 && crosshairAngle > 0) crosshairAngle += 0.1f;
				else if (crosshairAngle > 180 && crosshairAngle < 360) crosshairAngle -= 0.1f;
			}
			
			if (input.isKeyPressed(Input.KEY_ENTER)) {
				if(endTurn>0){
					continue;
				}
				if(currentPlayer.getCurrentBeaver().getCurrentWeapon().getAmmo()>0){
					currentPlayer.getCurrentBeaver().useWeapon();
					if (!powerSeqEngaged) {
						power = 1;
						powerSeqEngaged = true;
					}
					else {
						powerSeqEngaged = false;
						currentBeaverBody.setActive(false);
						bullet = new DynamicBody(new Circle(5),currentBeaverBody.getX(),currentBeaverBody.getY());
						bullet.setBullet(true);
						bullet.setRestitution(0);
						float x = (float) (Math.sin(Math.toRadians(crosshairAngle)));
						float y = (float) (Math.cos(Math.toRadians(crosshairAngle)));
						float xForce = (power/50)*(x)*10000000f;
						float yForce = (power/50)*(y)*10000000f;
						//System.out.println("x is "+x+", y is "+y+", angle at"+crosshairAngle);
						world.add(bullet);
						world.addBodyListener(bullet, this);
						bullet.applyForce(xForce, yForce);
						//Shot is fired, turn is over
						cameraOnBullet = true;
					}
				}

			}
			
			if (input.isKeyPressed(Input.KEY_SPACE)) {
				if (currentBeaverBody.getYVelocity() < 0.7 && currentBeaver.getFacingR()) {
					currentBeaverBody.applyForce(0, 1500000f);// /updates);
				}
				if (currentBeaverBody.getYVelocity() > -0.7 && !currentBeaver.getFacingR()) {
					currentBeaverBody.applyForce(0, 1500000f);// /updates);
				}
			}
			
			if (input.isKeyPressed(Input.KEY_ESCAPE)){
				sbg.enterState(BeaversGame.MAINMENUSTATE);
			}
			
			if (drawCircle) {
				addExplosionToTerrain((int) collisionX, (int) collisionY, this.radius);
				drawCircle = false;
			}
			
			if (System.currentTimeMillis() >= endTime || endTurn == 0) {
				endTurn = -1;
				endTurn(gc);
				cameraOnBullet = false;
			}
			
			if (endTurn > 0) endTurn--;
			world.update(1f / Constants.UPDATES_PER_SECOND);
		}
	}

	@Override
	public void mouseReleased(int button, int x, int y) {
		drawCircle = false;
		collisionX = (x - worldCenterX)/scale;
		collisionY = (y - worldCenterY)/-scale;
		System.out.println("Click @ "+(x-worldCenterX)+", "+-(y-worldCenterY)+".");
	}
	
	public int calculateDMG(double distance){
		int damage = (int) (80-distance);
		if(damage<0){
			damage=0;
		}
		return damage;
	}

	@Override
	public void collided(CollisionEvent event) {
		this.radius = currentPlayer.getCurrentBeaver().getCurrentWeapon().getBlastRadius();
		Body currentBeaverBody = currentPlayer.getCurrentBeaver().getBody();
		Beaver[] p1Team = p1.getTeam();
		Beaver[] p2Team = p2.getTeam();
		if (event.contains(bullet)) {
			
			WorldManifold worldManifold = new WorldManifold();
			event.getContact().jboxContact.getWorldManifold(worldManifold);

			drawCircle = true;
			collisionX = worldManifold.points[0].x;
			collisionY = worldManifold.points[0].y;
			
			float bulletX = bullet.getX();
			float bulletY = bullet.getY();
			//mark the bullet to be killed on the next update cycle
			killBullet = true;
			//mark the turn to be ended
			endTurn = 2000;

			//How much damage to do?
			for(int i = 0;i<Constants.BEAVERS_PER_TEAM;i++){
				double x1 = p1Team[i].getBody().getX();
				double y1 = p1Team[i].getBody().getY();
				double x2 = p2Team[i].getBody().getX();
				double y2 = p2Team[i].getBody().getY();
				double distanceB1 = distance(x1,y1,bulletX,bulletY);
				double distanceB2 = distance(x2,y2,bulletX,bulletY);
				if(distanceB1 < this.radius ){
					int damage = calculateDMG(distanceB1);
					p1Team[i].reduceHP(damage);
				}
				if(distanceB2 < this.radius){
					int damage = calculateDMG(distanceB1);
					p2Team[i].reduceHP(damage);
				}
			}
			
			
		}
		//if a body hits the boundary and it's a bullet, remove it.
		//it a beaver hits it, kill the beaver.
		if (event.contains(boundary)) {
			Body otherBody;
			if (event.getBodyA().equals(boundary)) otherBody = event.getBodyB();
			else otherBody = event.getBodyA();
			for(int i=0;i<Constants.BEAVERS_PER_TEAM;i++){
				if(p1Team[i].getBody()==otherBody){
					p1Team[i].reduceHP(1000);
					endTurn = 2000;
					//break;
				}
				if(p2Team[i].getBody()==otherBody){
					p2Team[i].reduceHP(1000);
					endTurn = 2000;
					//break;
				}
			}
			if (otherBody.equals(bullet)) {
				killBullet = true;
				endTurn = 2000;
			}	
		}
		//if the current beaver collects a health pack or ammo
		for (int i = 0; i < item.length; i++){
			if (event.contains(currentBeaverBody) && event.contains(item[i].getBody())) {
				if (item[i].isTypeAmmo()) currentPlayer.getCurrentBeaver().AddHP(100);
				if (item[i].isTypeHP()) currentPlayer.getCurrentBeaver().getCurrentWeapon().addAmmo(10);
				item[i].remove = true;
			}
		}
		
	}
	
	public double distance(double x1,double y1, double x2, double y2){
		double newX = x2 - x1;
		double newY = y2 - y1;
		newX = Math.pow(newX, 2);
		newY = Math.pow(newY,2);
		double value = Math.sqrt((newX+newY));
		return value;
	}
	
	@Override
	public void separated(CollisionEvent event) {
		Body currentBeaverBody = currentPlayer.getCurrentBeaver().getBody();
		if (event.contains(currentBeaverBody)) {
			drawCircle = false;
		}
	}
	
	protected StaticBody addBoundary(int width, int height){
		
		Polygon top = new Polygon();
		Polygon left = new Polygon();
		Polygon right = new Polygon();
		Polygon bottom = new Polygon();
		Vec2[] topP = {
				new Vec2(width, height),
				new Vec2(-width, height)
		};
		top.setPoints(topP);

		Vec2[] leftP = {
				new Vec2(-width, height),
				new Vec2(-width, -height)
		};
		left.setPoints(leftP);

		Vec2[] rightP = {
				new Vec2(width, -height),
				new Vec2(width, height)
		};
		right.setPoints(rightP);

		Vec2[] bottomP = {
				new Vec2(width, -height),
				new Vec2(-width, -height)
		};	
		bottom.setPoints(bottomP);

		CompoundShape boundaryShape = new CompoundShape();
		boundaryShape.add(top);
		boundaryShape.add(left);
		boundaryShape.add(right);
		boundaryShape.add(bottom);

		return new StaticBody(boundaryShape,0,0);
	}
	
}
